package drds.plus.api;

import drds.plus.common.Constants;
import drds.plus.common.model.Application;
import drds.plus.common.model.DataNode;
import drds.plus.common.properties.ConnectionProperties;
import drds.plus.executor.data_node_executor.DataNodeExecutorContext;
import drds.plus.executor.data_node_executor.nonspi.DataNodeExecutor;
import drds.plus.executor.data_node_executor.spi.DataNodeExecutorManager;
import drds.plus.executor.sequence_manager.SequenceManager;
import drds.plus.rule_engine.Route;
import drds.plus.sql_process.abstract_syntax_tree.configuration.manager.IndexManager;
import drds.plus.sql_process.abstract_syntax_tree.configuration.manager.SchemaManager;
import drds.plus.sql_process.abstract_syntax_tree.configuration.manager.StaticSchemaManager;
import drds.plus.sql_process.optimizer.IOptimizer;
import drds.plus.sql_process.optimizer.Optimizer;
import drds.plus.sql_process.optimizer.OptimizerContext;
import drds.plus.sql_process.optimizer.cost_esitimater.statistics.LocalStatisticsManager;
import drds.plus.sql_process.optimizer.cost_esitimater.statistics.StatisticsManager;
import drds.plus.sql_process.parser.SqlParseManager;
import drds.plus.sql_process.rule.IndexManagerImpl;
import drds.plus.sql_process.rule.RouteOptimizer;
import drds.plus.sql_process.rule.RuleSchemaManager;
import drds.plus.sql_process.rule.RuleStatisticsManager;
import drds.plus.sql_process.sequence.ISequenceManager;
import drds.plus.util.GeneralUtil;
import lombok.Data;

import java.util.Map;

/**
 * 依赖的组件
 */
@Data
public class ConfigHolder {


    private static final String GROUP_CONFIG_HOLDER_NAME = "GroupConfigHolder";
    private final boolean createDataNodeExecutor = true;
    private String applicationId;


    private RouteOptimizer routeOptimizer;
    private DataNodeExecutorManager dataNodeExecutorManager;
    private DataNodeExecutor dataNodeExecutor;
    private ISequenceManager sequenceManager = null;
    private SchemaManager schemaManager;
    private IndexManager indexManger;
    private IOptimizer optimizer;
    private SqlParseManager sqlParseManager;
    private OptimizerContext optimizerContext;
    private DataNodeExecutorContext dataNodeExecutorContext;
    private StatisticsManager statisticsManager = new RuleStatisticsManager(null, null);
    private Application application;
    private Map<String, Object> connectionProperties;


    public void doInit() throws RuntimeException {

        DataNodeExecutorContext dataNodeExecutorContext = new DataNodeExecutorContext();
        this.dataNodeExecutorContext = dataNodeExecutorContext;
        DataNodeExecutorContext.setExecutorContext(dataNodeExecutorContext);
        //
        OptimizerContext optimizerContext = new OptimizerContext();
        this.optimizerContext = optimizerContext;
        OptimizerContext.setOptimizerContext(optimizerContext);
        //
        topologyInit();
        ruleInit();
        schemaInit();
        optimizerInit();

        dataNodeExecutorContext.setDataNodeExecutorManager(dataNodeExecutorManager);
        dataNodeExecutorContext.setDataNodeExecutor(dataNodeExecutor);

        optimizerContext.setIndexManager(this.indexManger);
        optimizerContext.setApplication(dataNodeExecutorManager.getApplication());
        optimizerContext.setSchemaManager(schemaManager);
        optimizerContext.setRouteOptimizer(routeOptimizer);
        optimizerContext.setOptimizer(this.optimizer);
        optimizerContext.setSqlParseManager(this.sqlParseManager);
        optimizerContext.setStatisticsManager(this.statisticsManager);


        try {

            if (createDataNodeExecutor) {
                initDataNodeExecutor();
            }


            this.sequenceManager = new SequenceManager();
            sequenceManager.init();
            dataNodeExecutorContext.setSequenceManager(sequenceManager);
        } finally {

        }
    }


    protected void doDestroy() throws RuntimeException {
        schemaManager.destroy();
        routeOptimizer.destroy();
        optimizer.destroy();
        statisticsManager.destroy();
        dataNodeExecutorManager.destroy();
        dataNodeExecutor.destroy();
    }

    public void topologyInit() throws RuntimeException {
        dataNodeExecutorManager = new DataNodeExecutorManager(applicationId, this.connectionProperties);
        dataNodeExecutorManager.init();
        //
        dataNodeExecutorContext.setRepositoryNameToRepositoryMap(dataNodeExecutorManager.getRepositoryTypeToRepositoryMap());
        //
        dataNodeExecutor = new DataNodeExecutor();
        dataNodeExecutor.init();
        application = dataNodeExecutorManager.getApplication();
    }

    public void ruleInit() throws RuntimeException {
        Route route = new Route();
        route.setApplicationId(applicationId);
        if (application.getDataNodeList() != null) {
            String id = application.getDataNodeList().get(0).getId();
            route.setDefaultDataNodeId(id);
        }

        route.init();
        routeOptimizer = new RouteOptimizer(route);
        routeOptimizer.init();
    }

    public void schemaInit() throws RuntimeException {
        long tableMetaCacheExpireTime = (long) GeneralUtil.getExtraCmdLong(this.connectionProperties, ConnectionProperties.TABLE_META_CACHE_EXPIRE_TIME, 10000000);
        RuleSchemaManager ruleSchemaManager = new RuleSchemaManager(routeOptimizer, dataNodeExecutorManager.getApplication(), tableMetaCacheExpireTime);
        StaticSchemaManager staticSchemaManager = new StaticSchemaManager(applicationId);
        ruleSchemaManager.setStaticSchemaManager(staticSchemaManager);
        this.schemaManager = ruleSchemaManager;
        schemaManager.init();
        //
        IndexManager indexManager = new IndexManagerImpl(ruleSchemaManager);
        indexManager.init();
        this.indexManger = indexManager;

    }

    public void optimizerInit() throws RuntimeException {
        Optimizer optimizer = new Optimizer(routeOptimizer);
        optimizer.setExpireTime(GeneralUtil.getExtraCmdLong(this.connectionProperties, ConnectionProperties.OPTIMIZER_CACHE_EXPIRE_TIME, Constants.DEFAULT_OPTIMIZER_EXPIRE_TIME));
        optimizer.setCacheSize(GeneralUtil.getExtraCmdLong(this.connectionProperties, ConnectionProperties.OPTIMIZER_CACHE_SIZE, Constants.DEFAULT_OPTIMIZER_CACHE_SIZE));
        optimizer.init();

        this.optimizer = optimizer;
        this.sqlParseManager = optimizer.getSqlParseManager();
        LocalStatisticsManager localStatisticsManager = new LocalStatisticsManager();
        localStatisticsManager.init();
        this.statisticsManager = localStatisticsManager;
    }


    protected void initDataNodeExecutor() throws RuntimeException {
        for (DataNode dataNode : application.getDataNodeList()) {
            dataNodeExecutorManager.createDataNodeExecutor(dataNode);
        }
    }

}
